package com.tjmc.recruit.admin.service.impl;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.tjmc.company.api.entity.CompanyInfoEntity;
import com.tjmc.company.api.feign.CompanyInfoService;
import com.tjmc.recruit.admin.api.dto.PlatUserDTO;
import com.tjmc.recruit.admin.api.dto.UserInfo;
import com.tjmc.recruit.admin.api.entity.*;
import com.tjmc.recruit.admin.api.enums.PlatUserTypeEnum;
import com.tjmc.recruit.admin.mapper.PlatformUserInfoMapper;
import com.tjmc.recruit.admin.service.*;
import com.tjmc.recruit.common.core.constant.CacheConstants;
import com.tjmc.recruit.common.core.constant.CommonConstants;
import com.tjmc.recruit.common.core.constant.SecurityConstants;
import com.tjmc.recruit.common.core.exception.CheckedException;
import com.tjmc.recruit.common.core.exception.ErrorCodes;
import com.tjmc.recruit.common.core.exception.ValidateCodeException;
import com.tjmc.recruit.common.core.util.MsgUtils;
import com.tjmc.recruit.common.core.util.R;
import com.tjmc.recruit.common.core.util.RedisUtils;
import com.tjmc.recruit.common.security.util.SecurityUtils;
import com.tjmc.recruit.finance.api.entity.PlatformUserAccountEntity;
import com.tjmc.recruit.finance.api.feign.RemoteAccountService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeanUtils;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;


/**
 * 平台用户信息表
 *
 * @author recruit
 * @date 2024-10-29 14:41:11
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class PlatformUserInfoServiceImpl extends ServiceImpl<PlatformUserInfoMapper, PlatformUserInfoEntity> implements PlatformUserInfoService {
    private static final PasswordEncoder ENCODER = new BCryptPasswordEncoder();

    private final SysUserRoleService sysUserRoleService;
    private final SysRoleService sysRoleService;
    private final SysMenuService sysMenuService;
    private final SysPublicParamService sysPublicParamService;
    private final CompanyInfoService companyInfoServiceFeign;
    private final RemoteAccountService remoteAccountServiceFeign;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveUser(PlatformUserInfoEntity user) {
        // 2。密码加密存储
        PlatformUserInfoEntity platformUserInfo = new PlatformUserInfoEntity();
        BeanUtils.copyProperties(user, platformUserInfo);
        platformUserInfo.setDelFlag(CommonConstants.STATUS_NORMAL);
        platformUserInfo.setPassword(ENCODER.encode(user.getPassword()));

        // 4. 用户角色-根据用户类型判断
        PlatUserTypeEnum userType = user.getUserType();
        CompanyInfoEntity companyInfoEntity = new CompanyInfoEntity();
        if (userType.equals(PlatUserTypeEnum.PERSONAL)) {
            // 3. 用户类型
            platformUserInfo.setUserType(PlatUserTypeEnum.PERSONAL);
        } else {
            platformUserInfo.setUserType(PlatUserTypeEnum.COMPANY);
            //TODO 通过远程调用新增企业用户
            //公司全称取用户名
            companyInfoEntity.setFullName(platformUserInfo.getUsername());
            //联系人取注册姓名
            companyInfoEntity.setContactUserName(platformUserInfo.getName());
            //联系电话取注册手机号
            companyInfoEntity.setContactNumber(platformUserInfo.getPhone());
            //营业执照编号
            companyInfoEntity.setCertificationNo(platformUserInfo.getLicenseNo());
            //企业状态-未认证
            companyInfoEntity.setCertificationStatus(CommonConstants.STATUS_NORMAL);
            R<CompanyInfoEntity> save = companyInfoServiceFeign.add(companyInfoEntity, SecurityConstants.FROM_IN);
            if (save.getCode() != CommonConstants.SUCCESS) {
                throw new CheckedException(save.getMsg());
            }
            CompanyInfoEntity data = (CompanyInfoEntity) save.getData();
            //用户关联企业信息
            platformUserInfo.setCompanyId(data.getId());
            platformUserInfo.setCompanyName(data.getFullName());
            //账号类型:主账号
            platformUserInfo.setAccountType(PlatUserTypeEnum.MAIN);
        }
        //插入用户
        baseMapper.insert(platformUserInfo);
        //创建账户
        try {
            // 创建E币账户
            PlatformUserAccountEntity platformUserAccount = getPlatformUserAccount(platformUserInfo, companyInfoEntity);
            R r = remoteAccountServiceFeign.createAccount(platformUserAccount, SecurityConstants.FROM_IN);
            // 使用状态码判断是否成功
            if (r.getCode() != CommonConstants.SUCCESS) {
                throw new RuntimeException("创建E币账户失败");
            }
        } catch (Exception e) {
            log.error("创建E币账户异常", e);
            throw new RuntimeException("创建E币账户失败:" + e.getMessage());
        }
        return Boolean.TRUE;
    }

    private static @NotNull PlatformUserAccountEntity getPlatformUserAccount(PlatformUserInfoEntity platformUserInfo, CompanyInfoEntity companyInfoEntity) {
        PlatformUserAccountEntity platformUserAccount = new PlatformUserAccountEntity();
        platformUserAccount.setAccountId(platformUserInfo.getId());
        platformUserAccount.setCompanyId(companyInfoEntity.getId());
        platformUserAccount.setSubAccountNum(0);
        platformUserAccount.setSmsNum(0);
        platformUserAccount.setECoinNum(new BigDecimal(0));
        if (platformUserInfo.getUserType().equals(PlatUserTypeEnum.COMPANY)) {
            platformUserAccount.setPublishJobNum(1);
        }
        return platformUserAccount;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public R<Boolean> registerUser(PlatUserDTO userDto) {
        if (StrUtil.isBlank(userDto.getPhone())) {
            return R.failed("手机号不能为空");
        }
        PlatformUserInfoEntity userInfo = this.getOne(Wrappers.<PlatformUserInfoEntity>lambdaQuery()
                .eq(PlatformUserInfoEntity::getUsername, userDto.getUsername()));
        if (userInfo != null) {
            String message = MsgUtils.getMessage(ErrorCodes.SYS_USER_USERNAME_EXISTING, userDto.getUsername());
            return R.failed(message);
        }
        //查询手机号是否注册，仅允许同一个手机号分别注册企业和个人账号
        List<PlatformUserInfoEntity> userList = this.list(Wrappers.<PlatformUserInfoEntity>lambdaQuery()
                .eq(PlatformUserInfoEntity::getPhone, userDto.getPhone()));
        if (userList.isEmpty()) {
            return R.ok(saveUser(userDto));
        }

        List<PlatUserTypeEnum> list = userList.stream().map(PlatformUserInfoEntity::getUserType).toList();
        if (list.contains(userDto.getUserType())) {
            return R.failed("手机号已注册该类型用户");
        }
        checkValCode(userDto.getPhone(), userDto.getValCode());
        return R.ok(saveUser(userDto));
    }

    @Override
    public UserInfo findUserInfo(PlatformUserInfoEntity platformUserInfo) {
        UserInfo userInfo = new UserInfo();
        userInfo.setPlatformUserInfo(platformUserInfo);
        // 设置角色列表 （ID）
        List<Long> roleIds = sysRoleService.findRolesByUserId("PLAT_" + platformUserInfo.getId())
                .stream()
                .map(SysRole::getRoleId)
                .collect(Collectors.toList());
        userInfo.setRoles(ArrayUtil.toArray(roleIds, Long.class));

        // 设置权限列表（menu.permission）
        Set<String> permissions = new HashSet<>();
        roleIds.forEach(roleId -> {
            List<String> permissionList = sysMenuService.findMenuByRoleId(roleId)
                    .stream()
                    .filter(menu -> StrUtil.isNotEmpty(menu.getPermission()))
                    .map(SysMenu::getPermission)
                    .collect(Collectors.toList());
            permissions.addAll(permissionList);
        });
        userInfo.setPermissions(ArrayUtil.toArray(permissions, String.class));
        return userInfo;
    }

    @Override
    public R<Boolean> changePhone(String phone, String newPhone, String valCode, Boolean sync) {
        // 1. 校验验证码是否正确
        if (StrUtil.isBlank(newPhone)) {
            R.failed("变更手机号不能为空");
        }
        checkValCode(phone, valCode);
        String name = SecurityUtils.getUser().getName();
        PlatformUserInfoEntity user = this.getOne(Wrappers.<PlatformUserInfoEntity>lambdaQuery().eq(PlatformUserInfoEntity::getUsername, name));
        if (user == null) {
            return R.failed(MsgUtils.getMessage(ErrorCodes.SYS_USER_QUERY_ERROR));
        }
        user.setPhone(newPhone);
        baseMapper.updateById(user);
        return R.ok();
    }

    /**
     * 校验验证码
     *
     * @param phone   手机号
     * @param valCode 验证码
     */
    private static void checkValCode(String phone, String valCode) {
        if (StrUtil.isBlank(valCode)) {
            throw new ValidateCodeException("验证码不能为空");
        }
        String key = CacheConstants.DEFAULT_CODE_KEY + phone;
        if (!RedisUtils.hasKey(key)) {
            throw new ValidateCodeException("验证码不合法");
        }
        String cacheCode = RedisUtils.get(key);
        if (cacheCode == null) {
            throw new ValidateCodeException("未找到匹配验证码");
        }
        if (StrUtil.isBlank(cacheCode)) {
            RedisUtils.del(key);
            throw new ValidateCodeException("验证码不合法");
        }
        if (!StrUtil.equals(cacheCode, valCode)) {
            throw new ValidateCodeException("验证码错误");
        }
        RedisUtils.del(key);
    }

    @Override
    public R<Boolean> changeUserNameOrPassword(String userName, String valCode, String newPassword) {
        // 1.查询当前用户信息
        PlatformUserInfoEntity platformUserInfo = baseMapper.selectOne(Wrappers.<PlatformUserInfoEntity>lambdaQuery().eq(PlatformUserInfoEntity::getUsername, userName));
        if (platformUserInfo == null) {
            return R.failed(MsgUtils.getMessage(ErrorCodes.SYS_USER_QUERY_ERROR));
        }
        String phone = platformUserInfo.getPhone();
        //校验验证码
        checkValCode(phone, valCode);
        // 2. 修改用户名或密码
        if (StrUtil.isNotBlank(newPassword)) {
            platformUserInfo.setPassword(ENCODER.encode(newPassword));
        }
        if (StrUtil.isNotBlank(userName)) {
            platformUserInfo.setUsername(userName);
        }
        // 3. 更新信息
        baseMapper.updateById(platformUserInfo);
        return R.ok(true);
    }

    @Override
    public R<Boolean> changeWxUserNo(String wxUserNo) {
        PlatformUserInfoEntity platformUserInfo = baseMapper.selectOne(Wrappers.<PlatformUserInfoEntity>lambdaQuery().eq(PlatformUserInfoEntity::getUsername, SecurityUtils.getUser().getUsername()));
        if (platformUserInfo == null) {
            return R.failed(MsgUtils.getMessage(ErrorCodes.SYS_USER_QUERY_ERROR));
        }
        platformUserInfo.setWxUserNo(wxUserNo);
        baseMapper.updateById(platformUserInfo);
        return R.ok(true);
    }

    /**
     * 重置密码
     *
     * @param userInfo 用户信息
     * @return
     */
    @Override
    public R<Boolean> resetPassword(PlatformUserInfoEntity userInfo) {
        userInfo.setPassword(ENCODER.encode("123456"));
        baseMapper.updateById(userInfo);
        return R.ok(Boolean.TRUE);
    }

    @Override
    public Map<String, String> loginWithWeChat(String code) {
        String appId = sysPublicParamService.getSysPublicParamKeyToValue("WX_APP_ID");
        String appSecret = sysPublicParamService.getSysPublicParamKeyToValue("WX_APP_SECRET");

        String url = String.format(
                "https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code",
                appId, appSecret, code
        );

        RestTemplate restTemplate = new RestTemplate();
        String response = restTemplate.getForObject(url, String.class);

        ObjectMapper objectMapper = new ObjectMapper();
        try {
            JsonNode jsonNode = objectMapper.readTree(response);
            if (jsonNode.has("openid") && jsonNode.has("session_key")) {
                String openid = jsonNode.get("openid").asText();
                String sessionKey = jsonNode.get("session_key").asText();

                Map<String, String> result = new HashMap<>();
                result.put("openid", openid);
                result.put("session_key", sessionKey);

                return result;
            } else {
                log.error("WeChat API response does not contain openid or session_key: {}", response);

            }
        } catch (Exception e) {
            log.error("Failed to parse WeChat API response: " + e.getMessage());
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public String decryptPhoneNumber(String sessionKey, String encryptedData, String iv) {
        try {
            byte[] dataByte = Base64.getDecoder().decode(encryptedData);
            byte[] keyByte = Base64.getDecoder().decode(sessionKey);
            byte[] ivByte = Base64.getDecoder().decode(iv);

            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
            IvParameterSpec ivSpec = new IvParameterSpec(ivByte);
            cipher.init(Cipher.DECRYPT_MODE, spec, ivSpec);

            byte[] resultByte = cipher.doFinal(dataByte);
            if (resultByte != null && resultByte.length > 0) {
                String result = new String(resultByte, "UTF-8");
                log.info("Decryption result: {}", result); // 打印解密后的完整数据

                ObjectMapper objectMapper = new ObjectMapper();
                JsonNode jsonNode = objectMapper.readTree(result);

                // 验证解密后数据的完整性
                if (jsonNode.has("phoneNumber")) {
                    return jsonNode.get("phoneNumber").asText();
                } else {
                    log.warn("Decrypted data does not contain phoneNumber: {}", result);
                }
            } else {
                log.warn("Decryption resulted in empty or null byte array.");
            }
        } catch (Exception e) {
            log.error("Failed to decrypt phone number: {}", e.getMessage());
            e.printStackTrace();
        }
        return null;
    }


}
